package model.cliente;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;


import java.util.ArrayList;
import java.util.Set;

import model.prestamos.Prestamo;

import org.junit.Before;
import org.junit.Test;

import excepciones.cliente.ClienteGrupalInvalidoException;
import excepciones.cliente.ClienteGrupalNoPuedeSerModificadoException;


/**
 * Test suite de la clase ClienteGrupal.
 * @author Emanuel
 *
 */

public class ClienteGrupalTest extends ClienteTest {
	
	private ClienteIndividual mockRepresentante;
	private Cliente mockClienteDelRepresentante;
	private Cliente mockCliente1;
	private Cliente mockCliente2;
	private Prestamo mockPrestamoActivo;
	private Prestamo mockPrestamoSolicitado;
	private ClienteGrupal grupo;
	private ClienteGrupal grupoGrande;

	@Before
	public void setUp() throws Exception {
		super.setUp();
		
		this.mockRepresentante=mock(ClienteIndividual.class);
		this.mockClienteDelRepresentante=mock(Cliente.class);
		when(this.mockClienteDelRepresentante.contieneACliente(this.mockRepresentante)).thenReturn(true);
		this.mockCliente1=mock(Cliente.class);
		this.mockCliente2=mock(Cliente.class);
	
		this.mockPrestamoActivo=mock(Prestamo.class);
		when(this.mockPrestamoActivo.esActivo()).thenReturn(true);
		this.mockPrestamoSolicitado=mock(Prestamo.class);
		when(this.mockPrestamoSolicitado.esSolicitado()).thenReturn(true);
		
		this.grupo=new ClienteGrupal(this.mockBanco, this.mockRepresentante, this.mockClienteDelRepresentante, this.mockCliente1);
		
		ArrayList<Cliente> miembros=new ArrayList<Cliente>();
		miembros.add(this.mockClienteDelRepresentante);
		miembros.add(this.mockCliente1);
		miembros.add(this.mockCliente2);
		this.grupoGrande=new ClienteGrupal(this.mockBanco, this.mockRepresentante, miembros);
	}
	
	/**
	 * 
	 * @throws ClienteGrupalInvalidoException
	 */
	
	@Test(expected=ClienteGrupalInvalidoException.class)
	public void testCreacionConDosMiembrosIguales() throws ClienteGrupalInvalidoException{
		new ClienteGrupal(this.mockBanco, this.mockRepresentante, this.mockClienteDelRepresentante, this.mockClienteDelRepresentante);
		fail("Se debio levantar una excepcion!");
	}
	
	/**
	 * 
	 * @throws ClienteGrupalInvalidoException
	 */
	
	@Test(expected=ClienteGrupalInvalidoException.class)
	public void testCreacionConRepresentanteNoEnGrupo() throws ClienteGrupalInvalidoException{
		new ClienteGrupal(this.mockBanco, this.mockRepresentante, this.mockCliente1, this.mockCliente2);
		fail("Se debio levantar una excepcion!");
	}
	
	/**
	 * 
	 * @throws ClienteGrupalInvalidoException
	 */
	
	@Test(expected=ClienteGrupalInvalidoException.class)
	public void testCreacionPorColeccionConUnUnicoMiembro() throws ClienteGrupalInvalidoException{
		ArrayList<Cliente> miembros=new ArrayList<Cliente>();
		miembros.add(this.mockRepresentante);
		new ClienteGrupal(this.mockBanco, this.mockRepresentante, miembros);
		fail("Se debio levantar una excepcion!");
	}
	
	/**
	 * 
	 * @throws ClienteGrupalInvalidoException
	 */
	
	@Test(expected=ClienteGrupalInvalidoException.class)
	public void testCreacionPorColeccionConRepresentanteNoEnGrupo() throws ClienteGrupalInvalidoException{
		ArrayList<Cliente> miembros=new ArrayList<Cliente>();
		miembros.add(this.mockCliente1);
		miembros.add(this.mockCliente2);
		new ClienteGrupal(this.mockBanco, this.mockRepresentante, miembros);
		fail("Se debio levantar una excepcion!");
	}
	
	@Test
	public void testEqualsPorIdentidad(){
		assertEquals(this.grupo,this.grupo);
	}
	
	@Test
	public void testEqualsPorContenido() throws ClienteGrupalInvalidoException{
		ArrayList<Cliente> miembros=new ArrayList<Cliente>();
		miembros.add(this.mockClienteDelRepresentante);
		miembros.add(this.mockCliente1);
		ClienteGrupal elMismoGrupo=new ClienteGrupal(this.mockBanco, this.mockRepresentante, miembros);
		assertEquals(this.grupo,elMismoGrupo);
	}
	
	@Test
	public void testNoEquals(){
		//assertNotEquals(this.grupo,new Object());
		assertFalse(this.grupo.equals(new Object()));
	}
	
	/**
	 * 
	 */
	
	@Test
	public void testGetMiembros(){
		Set<Cliente> miembros=this.grupo.getMiembros();
		assertTrue(miembros.contains(mockClienteDelRepresentante));
		assertTrue(miembros.contains(mockCliente1));
	}
	
	/**
	 * 
	 */
	
	@Test
	public void testGetRepresentante(){
		assertSame(this.grupo.getRepresentante(),this.mockRepresentante);
	}
	/**
	 * 
	 */
	@Test
	public void testAgregarPrestamo(){
		this.grupo.agregarPrestamo(this.mockPrestamoActivo);
		assertTrue(this.grupo.getPrestamos().contains(this.mockPrestamoActivo));
	}
	
	/**
	 * 
	 * @throws ClienteGrupalNoPuedeSerModificadoException
	 */
	
	@Test
	public void testCambiarRepresentante() throws ClienteGrupalNoPuedeSerModificadoException{
		ClienteIndividual mockNuevoRepresentante=mock(ClienteIndividual.class);
		when(this.mockCliente1.contieneACliente(mockNuevoRepresentante)).thenReturn(true);
		this.grupo.cambiarRepresentante(mockNuevoRepresentante);
		assertSame(this.grupo.getRepresentante(),mockNuevoRepresentante);
	}
	
	/**
	 * 
	 * @throws ClienteGrupalNoPuedeSerModificadoException
	 */
	
	@Test(expected=ClienteGrupalNoPuedeSerModificadoException.class)
	public void testCambiarRepresentanteFallaPorNuevoRepresentanteNoEnGrupo() throws ClienteGrupalNoPuedeSerModificadoException{
		ClienteIndividual mockNuevoRepresentante=mock(ClienteIndividual.class);
		this.grupo.cambiarRepresentante(mockNuevoRepresentante);
		fail("Se debio levantar una excepcion!");
	}
	
	/**
	 * 
	 * @throws ClienteGrupalNoPuedeSerModificadoException
	 */
	
	@Test(expected=ClienteGrupalNoPuedeSerModificadoException.class)
	public void testCambiarRepresentanteFallaPorPrestamoActivo() throws ClienteGrupalNoPuedeSerModificadoException{
		ClienteIndividual mockNuevoRepresentante=mock(ClienteIndividual.class);
		when(this.mockCliente1.contieneACliente(mockNuevoRepresentante)).thenReturn(true);
		this.grupo.agregarPrestamo(this.mockPrestamoActivo);
		this.grupo.cambiarRepresentante(mockNuevoRepresentante);
		fail("Se debio levantar una excepcion!");
	}
	
	/**
	 * 
	 * @throws ClienteGrupalNoPuedeSerModificadoException
	 */
	
	@Test(expected=ClienteGrupalNoPuedeSerModificadoException.class)
	public void testCambiarRepresentanteFallaPorPrestamoSolicitado() throws ClienteGrupalNoPuedeSerModificadoException{
		ClienteIndividual mockNuevoRepresentante=mock(ClienteIndividual.class);
		when(this.mockCliente1.contieneACliente(mockNuevoRepresentante)).thenReturn(true);
		this.grupo.agregarPrestamo(this.mockPrestamoSolicitado);
		this.grupo.cambiarRepresentante(mockNuevoRepresentante);
		fail("Se debio levantar una excepcion!");
	}
	
	/**
	 * 
	 * @throws ClienteGrupalNoPuedeSerModificadoException
	 */
	
	@Test
	public void testAgregarMiembro() throws ClienteGrupalNoPuedeSerModificadoException{
		this.grupo.agregarMiembro(this.mockCliente2);
		assertTrue(this.grupo.getMiembros().contains(mockCliente2));
	}
	
	/**
	 * 
	 * @throws ClienteGrupalNoPuedeSerModificadoException
	 */
	
	@Test(expected=ClienteGrupalNoPuedeSerModificadoException.class)
	public void testAgregarMiembroFallaPorPrestamoActivo() throws ClienteGrupalNoPuedeSerModificadoException{
		this.grupo.agregarPrestamo(this.mockPrestamoActivo);
		this.grupo.agregarMiembro(this.mockCliente2);
		fail("Se debio levantar una excepcion!");
	}
	
	/**
	 * 
	 * @throws ClienteGrupalNoPuedeSerModificadoException
	 */
	
	@Test(expected=ClienteGrupalNoPuedeSerModificadoException.class)
	public void testAgregarMiembroFallaPorPrestamoSolicitado() throws ClienteGrupalNoPuedeSerModificadoException{
		this.grupo.agregarPrestamo(this.mockPrestamoSolicitado);
		this.grupo.agregarMiembro(this.mockCliente2);
		fail("Se debio levantar una excepcion!");
	}
	
	/**
	 * 
	 * @throws ClienteGrupalNoPuedeSerModificadoException
	 */
	
	@Test
	public void testQuitarMiembro() throws ClienteGrupalNoPuedeSerModificadoException{
		this.grupoGrande.quitarMiembro(this.mockCliente2);
		assertFalse(this.grupoGrande.getMiembros().contains(mockCliente2));
	}
	
	/**
	 * 
	 * @throws ClienteGrupalNoPuedeSerModificadoException
	 */
	
	@Test(expected=ClienteGrupalNoPuedeSerModificadoException.class)
	public void testQuitarMiembroFallaPorPrestamoActivo() throws ClienteGrupalNoPuedeSerModificadoException{
		this.grupoGrande.agregarPrestamo(this.mockPrestamoActivo);
		this.grupoGrande.quitarMiembro(this.mockCliente2);
		fail("Se debio levantar una excepcion!");
	}
	
	/**
	 * 
	 * @throws ClienteGrupalNoPuedeSerModificadoException
	 */
	
	@Test(expected=ClienteGrupalNoPuedeSerModificadoException.class)
	public void testQuitarMiembroFallaPorPrestamoSolicitado() throws ClienteGrupalNoPuedeSerModificadoException{
		this.grupoGrande.agregarPrestamo(this.mockPrestamoSolicitado);
		this.grupoGrande.quitarMiembro(this.mockCliente2);
		fail("Se debio levantar una excepcion!");
	}
	
	/**
	 * 
	 * @throws ClienteGrupalNoPuedeSerModificadoException
	 */
	
	@Test(expected=ClienteGrupalNoPuedeSerModificadoException.class)
	public void testQuitarMiembroFallaPorRepresentanteEnExMiembro() throws ClienteGrupalNoPuedeSerModificadoException{
		this.grupoGrande.quitarMiembro(this.mockClienteDelRepresentante);
		fail("Se debio levantar una excepcion!");
	}
	
	/**
	 * 
	 * @throws ClienteGrupalNoPuedeSerModificadoException
	 */
	
	@Test(expected=ClienteGrupalNoPuedeSerModificadoException.class)
	public void testQuitarMiembroFallaPorCantidadDeMiembrosDebajoDelLimite() throws ClienteGrupalNoPuedeSerModificadoException{
		this.grupo.quitarMiembro(this.mockCliente1);
		fail("Se debio levantar una excepcion!");
	}
	
	/**
	 * 
	 */
	
	@Test
	public void testAptoParaPrestamo(){
		when(this.mockClienteDelRepresentante.aptoParaPrestamo()).thenReturn(true);
		when(this.mockCliente1.aptoParaPrestamo()).thenReturn(true);
		
		assertTrue(this.grupo.aptoParaPrestamo());
		verify(this.mockClienteDelRepresentante).aptoParaPrestamo();
		verify(this.mockCliente1).aptoParaPrestamo();
	}
	
	/**
	 * 
	 */
	
	@Test
	public void testNoAptoParaPrestamo(){
		when(this.mockClienteDelRepresentante.aptoParaPrestamo()).thenReturn(false);
		when(this.mockCliente1.aptoParaPrestamo()).thenReturn(true);
		
		assertFalse(this.grupo.aptoParaPrestamo());
		verify(this.mockClienteDelRepresentante).aptoParaPrestamo();
	}
	
	/**
	 *  Verifica que se notifico la nueva cuota del prestamo actual.
	 */
	
    @Test
    public void testNotificarNuevaCuota(){
            this.grupo.notificarNuevaCuotaDe(this.mockPrestamoActivo);
            verify(this.mockRepresentante).notificarNuevaCuotaDe(this.mockPrestamoActivo);
    }

	/**
	 * Testea que el nombre del cliente no comienza con una determinada
	 * cadena de strings.
	 */
    
	@Test
	public void testNombreClienteComienzaCon(){
		String criterio="Gon";
		when(this.mockCliente1.nombreClienteComienzaCon(criterio)).thenReturn(true);
		assertTrue(this.grupo.nombreClienteComienzaCon(criterio));
		verify(this.mockCliente1).nombreClienteComienzaCon(criterio);
	}
	
	/**
	 * Testea que el nombre del cliente no comienza con una determinada
	 * cadena de strings.
	 */
	
	@Test
	public void testNombreClienteNoComienzaCon(){
		String criterio="Gon";
		when(this.mockCliente1.nombreClienteComienzaCon(criterio)).thenReturn(false);
		assertFalse(this.grupo.nombreClienteComienzaCon(criterio));
		verify(this.mockCliente1).nombreClienteComienzaCon(criterio);
	}
	
	/**
	 * Testea que el cliente tiene dni
	 */
	
	@Test
	public void testClienteTieneDNI(){
		int criterio=5486184;
		when(this.mockCliente1.clienteTieneDNI(criterio)).thenReturn(true);
		assertTrue(this.grupo.clienteTieneDNI(criterio));
		verify(this.mockCliente1).clienteTieneDNI(criterio);
	}
	
	/**
	 * Testea que el cliente no tiene dmi
	 */
	
	@Test
	public void testClienteNoTieneDNI(){
		int criterio=5486184;
		when(this.mockCliente1.clienteTieneDNI(criterio)).thenReturn(false);
		assertFalse(this.grupo.clienteTieneDNI(criterio));
		verify(this.mockCliente1).clienteTieneDNI(criterio);
	}
	
	/**
	 * Testea que el cliente ha cambiado de tipoCliente.
	 */
	
	@Test
	public void testUpdateTipo(){
		this.grupo.updateTipo();
		verify(this.mockClienteDelRepresentante).updateTipo();
		verify(this.mockCliente1).updateTipo();
	}
	
	/**
	 * Testea que el grupo ya no es apto para pedir prestamos
	 * por estar en estado Incobrable.
	 */
	
	@Test
	public void testYaNoEsAptoPorIncobrable(){
		this.grupo.yaNoEsAptoPorIncobrable();
		verify(this.mockClienteDelRepresentante).yaNoEsAptoPorIncobrable();;
		verify(this.mockCliente1).yaNoEsAptoPorIncobrable();;
	}
	
}
